<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\UX\Autocomplete\Tests\Fixtures;

use Composer\InstalledVersions;
use Doctrine\Bundle\DoctrineBundle\DoctrineBundle;
use Doctrine\ORM\Mapping\AssociationMapping;
use Psr\Log\NullLogger;
use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
use Symfony\Bundle\MakerBundle\MakerBundle;
use Symfony\Bundle\SecurityBundle\SecurityBundle;
use Symfony\Bundle\TwigBundle\TwigBundle;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Kernel as BaseKernel;
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\UX\Autocomplete\AutocompleteBundle;
use Symfony\UX\Autocomplete\DependencyInjection\AutocompleteFormTypePass;
use Symfony\UX\Autocomplete\Tests\Fixtures\Autocompleter\CustomAttributesProductAutocompleter;
use Symfony\UX\Autocomplete\Tests\Fixtures\Autocompleter\CustomGroupByProductAutocompleter;
use Symfony\UX\Autocomplete\Tests\Fixtures\Autocompleter\CustomProductAutocompleter;
use Symfony\UX\Autocomplete\Tests\Fixtures\Form\CategoryWithCallbackAsCustomValue;
use Symfony\UX\Autocomplete\Tests\Fixtures\Form\CategoryWithPropertyNameAsCustomValue;
use Symfony\UX\Autocomplete\Tests\Fixtures\Form\ProductType;
use Twig\Environment;
use Zenstruck\Foundry\ZenstruckFoundryBundle;

final class Kernel extends BaseKernel
{
    use MicroKernelTrait;

    private bool $enableForms = true;

    public function disableForms(): void
    {
        $this->enableForms = false;
    }

    public function testForm(FormFactoryInterface $formFactory, Environment $twig, Request $request): Response
    {
        $form = $formFactory->create(ProductType::class);
        $form->handleRequest($request);

        return new Response($twig->render('form.html.twig', [
            'form' => $form->createView(),
        ]));
    }

    public function registerBundles(): iterable
    {
        yield new FrameworkBundle();
        yield new TwigBundle();
        yield new DoctrineBundle();
        yield new AutocompleteBundle();
        yield new SecurityBundle();
        yield new MakerBundle();
        yield new ZenstruckFoundryBundle();
    }

    protected function build(ContainerBuilder $container): void
    {
        // workaround https://github.com/symfony/symfony/issues/50322
        $container->addCompilerPass(new class() implements CompilerPassInterface {
            public function process(ContainerBuilder $container): void
            {
                $container->removeDefinition('doctrine.orm.listeners.pdo_session_handler_schema_listener');

                if (\PHP_VERSION_ID >= 80400 && $container->hasParameter('doctrine.orm.proxy_dir')) {
                    // Workaround for `RuntimeException: Unable to create the Doctrine Proxy directory "". in vendor/symfony/doctrine-bridge/CacheWarmer/ProxyCacheWarmer.php:49`
                    // when running PHP 8.4 and Doctrine ORM 3.5+.
                    $container->getDefinition('doctrine.orm.default_configuration')
                        ->addMethodCall('setProxyDir', ['%doctrine.orm.proxy_dir%']);
                }
            }
        }, PassConfig::TYPE_BEFORE_OPTIMIZATION, 1);
    }

    protected function configureContainer(ContainerConfigurator $c): void
    {
        $c->extension('framework', [
            'secret' => 'S3CRET',
            'http_method_override' => false,
            'test' => true,
            'router' => ['utf8' => true],
            'secrets' => false,
            'session' => ['storage_factory_id' => 'session.storage.factory.mock_file'],
            'form' => ['enabled' => $this->enableForms],
        ]);

        $c->extension('twig', [
            'default_path' => '%kernel.project_dir%/tests/Fixtures/templates',
        ]);

        $c->extension('zenstruck_foundry', []);

        $doctrineConfig = [
            'dbal' => ['url' => '%env(resolve:DATABASE_URL)%'],
            'orm' => [
                'auto_mapping' => true,
                'mappings' => [
                    'Test' => [
                        'is_bundle' => false,
                        'dir' => '%kernel.project_dir%/tests/Fixtures/Entity',
                        'prefix' => 'Symfony\UX\Autocomplete\Tests\Fixtures\Entity',
                        'type' => 'attribute',
                        'alias' => 'Test',
                    ],
                ],
            ],
        ];

        if (null !== $doctrineBundleVersion = InstalledVersions::getVersion('doctrine/doctrine-bundle')) {
            if (version_compare($doctrineBundleVersion, '3.0.0', '<')) {
                $doctrineConfig['orm']['auto_generate_proxy_classes'] = true;

                if (version_compare($doctrineBundleVersion, '2.8.0', '>=')) {
                    $doctrineConfig['orm']['enable_lazy_ghost_objects'] = true;
                }

                if (version_compare($doctrineBundleVersion, '2.12.0', '>=')) {
                    $doctrineConfig['orm']['controller_resolver']['auto_mapping'] = true;
                }
            }

            if (\PHP_VERSION_ID >= 80400 && version_compare($doctrineBundleVersion, '2.15.0', '>=') && version_compare($doctrineBundleVersion, '4.0.0', '<')) {
                $doctrineConfig['orm']['enable_native_lazy_objects'] = true;
            }
        }

        $c->extension('doctrine', $doctrineConfig);

        $c->extension('security', [
            'password_hashers' => [
                PasswordAuthenticatedUserInterface::class => 'plaintext',
            ],
            'providers' => [
                'users_in_memory' => [
                    'memory' => [
                        'users' => [
                            'mr_autocompleter' => ['password' => 'symfonypass', 'roles' => ['ROLE_USER']],
                        ],
                    ],
                ],
            ],
            'firewalls' => [
                'main' => [
                    'http_basic' => true,
                ],
            ],
        ]);

        $services = $c->services();
        $services
            ->defaults()
                ->autowire()
                ->autoconfigure()
            // disable logging errors to the console
            ->set('logger', NullLogger::class)
            ->load(__NAMESPACE__.'\\', __DIR__)
            ->exclude(['Kernel.php'])
        ;

        $services->set(CustomProductAutocompleter::class)
            ->public()
            ->arg(1, new Reference('ux.autocomplete.entity_search_util'))
            ->tag(AutocompleteFormTypePass::ENTITY_AUTOCOMPLETER_TAG, [
                'alias' => 'custom_product',
            ]);

        $services->set(CustomGroupByProductAutocompleter::class)
            ->public()
            ->arg(1, new Reference('ux.autocomplete.entity_search_util'))
            ->tag(AutocompleteFormTypePass::ENTITY_AUTOCOMPLETER_TAG, [
                'alias' => 'custom_group_by_product',
            ]);

        $services->set(CustomAttributesProductAutocompleter::class)
            ->public()
            ->arg(1, new Reference('ux.autocomplete.entity_search_util'))
            ->tag(AutocompleteFormTypePass::ENTITY_AUTOCOMPLETER_TAG, [
                'alias' => 'custom_attributes_product',
            ]);

        $services->alias('public.results_executor', 'ux.autocomplete.results_executor')
            ->public();

        $services->alias('public.ux.autocomplete.make_autocomplete_field', 'ux.autocomplete.make_autocomplete_field')
            ->public();

        $services->set(CategoryWithPropertyNameAsCustomValue::class)
            ->tag('ux.entity_autocomplete_field')
            ->public()
        ;

        $services->set(CategoryWithCallbackAsCustomValue::class)
            ->tag('ux.entity_autocomplete_field')
            ->public()
        ;
    }

    protected function configureRoutes(RoutingConfigurator $routes): void
    {
        $routes->import('@AutocompleteBundle/config/routes.php')
            ->prefix('/test/autocomplete');

        $routes->add('test_form', '/test-form')->controller('kernel::testForm');

        $routes->add('ux_autocomplete_alternate', '/alt/test/autocomplete/{alias}')
            ->controller('ux.autocomplete.entity_autocomplete_controller');
    }
}
